package org.eclipseuseradmin.internal.ui.security;

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import org.eclipseuseradmin.internal.ui.UserAdminUiPlugin;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;

public class UserAdminLoginModule implements LoginModule {

	private boolean loggedin = false;
	
	private Subject subject;

	private CallbackHandler callbackHandler;

	private User user;

	public boolean abort() throws LoginException {

		return false;
	}

	public boolean commit() throws LoginException {

		String name = user.getName();
		String[] roles = getRoles(user);

		subject.getPrincipals().add(getPrincipal(name));
		subject.getPrincipals().add(getRolesPrincipal(roles));
		subject.getPrivateCredentials().add(
				user.getCredentials().get("password"));

		loggedin = true;
		return true;
	}

	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map<String, ?> sharedState, Map<String, ?> options) {

		this.subject = subject;
		this.callbackHandler = callbackHandler;
	}

	public boolean login() throws LoginException {

		if (loggedin) {
			return false;
		}
		
		ConfirmationCallback confirmationCallback = new ConfirmationCallback(
				ConfirmationCallback.INFORMATION,
				ConfirmationCallback.OK_CANCEL_OPTION,
				ConfirmationCallback.CANCEL);
		TextOutputCallback messageCallback = new TextOutputCallback(
				TextOutputCallback.INFORMATION, "enter credential");
		NameCallback nameCallback = new NameCallback("name");
		PasswordCallback passwordCallback = new PasswordCallback("password",
				false);

		return login(confirmationCallback, messageCallback, nameCallback,
				passwordCallback);
	}

	private boolean login(ConfirmationCallback confirmationCallback,
			TextOutputCallback messageCallback, NameCallback nameCallback,
			PasswordCallback passwordCallback) throws LoginException {

		try {
			callbackHandler.handle(new Callback[] { confirmationCallback,
					messageCallback, nameCallback, passwordCallback });
		} catch (IOException e) {
			throw new LoginException();
		} catch (UnsupportedCallbackException e) {
			throw new LoginException();
		}

		if (confirmationCallback.getSelectedIndex() == ConfirmationCallback.CANCEL) {
			throw new LoginException();
		}

		UserAdmin userAdmin = getUserAdmin();
		user = (User) userAdmin.getRole(nameCallback.getName());
		if (user == null
				|| !user.hasCredential("password", new String(passwordCallback
						.getPassword()))) {

			messageCallback = new TextOutputCallback(TextOutputCallback.ERROR,
					"authentication error");
			nameCallback = new NameCallback("name");
			passwordCallback = new PasswordCallback("password", false);
			login(confirmationCallback, messageCallback, nameCallback,
					passwordCallback);
		}

		return true;
	}

	public boolean logout() throws LoginException {

		if (!loggedin) {
			return false;
		}

		subject.getPrincipals().clear();
		subject.getPublicCredentials().clear();
		subject.getPrivateCredentials().clear();
		user = null;
		
		loggedin = false;
		return true;
	}

	private RolePrincipal getPrincipal(String name) {

		return new RolePrincipal(name);
	}

	private RolesPrincipal getRolesPrincipal(String[] roles) {

		RolesPrincipal rolesPrincipal = new RolesPrincipal("Roles");

		if (roles != null) {

			for (String roleName : roles) {
				rolesPrincipal.addMember(new RolePrincipal(roleName));
			}
		}
		return rolesPrincipal;
	}

	private String[] getRoles(User user) {

		UserAdmin userAdmin = getUserAdmin();
		Authorization authorization = userAdmin.getAuthorization(user);

		return authorization.getRoles();
	}

	private UserAdmin getUserAdmin() {

		return UserAdminUiPlugin.getUserAdmin();
	}
}
